package com.guodi.map.controller;

import com.guodi.common.constant.StatusCode;
import com.guodi.common.utils.UuidUtil;
import com.guodi.core.boot.ctrl.BaseController;
import com.guodi.core.minio.util.MinioUtil;
import com.guodi.core.tool.api.R;
import com.guodi.core.tool.utils.Func;
import com.guodi.map.entity.MapProjectFile;
import com.guodi.map.service.IMapCommonService;
import com.guodi.map.service.IMapProjectFileService;
import com.guodi.map.utils.RecursionUtil;
import com.guodi.map.utils.VerificationUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @描述：附件/目录信息表控制类
 * @作者: 代鹏刚
 * @日期: 2021-01-19
 */
@RestController
@RequestMapping("/MapProjectFile")
@Api(value = "API - MapProjectFileController", tags = "附件目录信息表")
public class MapProjectFileController extends BaseController {

    @Autowired
    public IMapProjectFileService mapProjectFileService;
    @Autowired
    private IMapCommonService commonService;

    /**
    * @描述: 返回附件目录树
    * @入参:
    * @出参:
    * @作者: 代鹏刚
    * @日期: 2021-01-19
    **/
    @PostMapping("/listTreeByEntity")
    @ApiOperation(value = " 返回附件目录树")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentId", value = "专题目录ID", required = true, dataType = "string", paramType = "query")
    })
    public R listTreeByEntity(String parentId){
        List<MapProjectFile> mapProjectFiles = mapProjectFileService.listTreeByEntity(parentId);
        return R.data(RecursionUtil.buildMapProjectFileTree(mapProjectFiles,parentId));
    }

    /**
     * @描述: 新增
     * @入参:
     * @出参:
     * @作者: 代鹏刚
     * @日期: 2021-01-19
     **/
    @PostMapping("/save")
    @ApiOperation(value = "新增")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "目录/附件名称", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "parentId", value = "上级目录id", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "sort", value = "排序", dataType = "int", paramType = "query"),
    })
    public R save(@ApiIgnore MapProjectFile mapProjectFile, @RequestParam(required = false, value = "file") MultipartFile file) throws Exception{
        prepareData(mapProjectFile, true);
        mapProjectFile.setId(UuidUtil.get32UUID());
        if(null != file){
            // 文件上传
            int index = file.getOriginalFilename().lastIndexOf(".");
            String fileType = file.getOriginalFilename().substring(index + 1).toLowerCase();
            String fileId = MinioUtil.uploadFile(file.getInputStream(),"map-project-file", file.getOriginalFilename());
            if(StringUtils.isBlank(fileId)){
                return R.fail(StatusCode.REQUEST_CODE_OPERATION_FAILURE,"数据上传失败！");
            }
            mapProjectFile.setFileSize((double) file.getSize());
            mapProjectFile.setFileFormat(fileType);
            mapProjectFile.setFilePath(fileId);
            mapProjectFile.setType(2);
            //获取上传原始文件名
            if (StringUtils.isBlank(mapProjectFile.getName())) {
                String uploadName = file.getOriginalFilename().substring(0, index);
                mapProjectFile.setName(uploadName);
            }
        }else {
            if (StringUtils.isBlank(mapProjectFile.getName())) {
                return R.fail(StatusCode.ERROR_CODE_40005, "请传入目录名称");
            }
            // 新增目录
            List<MapProjectFile> mapProjectFiles = mapProjectFileService.listByMap(VerificationUtil.verifyMap(mapProjectFile.getName(),mapProjectFile.getParentId()));
            if(mapProjectFiles.size() > 0){
                return R.fail(StatusCode.ERROR_CODE_40005, "该目录名称已经存在");
            }
            mapProjectFile.setType(1);
        }
        //添加排序信息
        if (mapProjectFile.getSort() == null) {
            Integer sort = commonService.getMaxSort(mapProjectFile.getParentId(),"map_project_file");
            mapProjectFile.setSort(sort + 1);
        }
        mapProjectFileService.save(mapProjectFile);
        return R.data(mapProjectFile);
    }

    /**
     * @描述: 修改目录
     * @入参:
     * @出参:
     * @作者: 代鹏刚
     * @日期: 2021-01-19
     **/
    @PostMapping("/update")
    @ApiOperation(value = "修改目录")
    public R update(MapProjectFile mapProjectFile) {
        prepareData(mapProjectFile, false);
        // 查询当前记录
        MapProjectFile byId = mapProjectFileService.getById(mapProjectFile.getId());
        // 查询当前级别下是否还有别的同名记录
        List<MapProjectFile> mapProjectFiles = mapProjectFileService.listByMap(VerificationUtil.verifyMap(mapProjectFile.getName(),byId.getParentId()));
        if(mapProjectFiles.size() > 0 && !mapProjectFiles.get(0).getId().equals(mapProjectFile.getId())){
            return R.fail(StatusCode.ERROR_CODE_40005, "该目录名称已经存在");
        }
        mapProjectFileService.updateById(mapProjectFile);
        return R.data(mapProjectFile);
    }

    /**
     * @描述: 根据主键删除
     * @入参: ids
     * @出参:
     * @作者: 代鹏刚
     * @日期: 2021-01-19
     **/
    @PostMapping("/delete")
    @ApiOperation(value = "根据主键删除")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "ID集合(多个使用逗号隔开)", required = true, dataType = "string", paramType = "query")
    })
    public R delete(String ids){
        // 获取目录下所有的附件信息
        List<MapProjectFile> mapProjectFiles = mapProjectFileService.getAllChildrenById(Func.toStrList(ids), 2);
        // 删除FDFS附件信息
        if(mapProjectFiles.size() > 0){
            for (MapProjectFile mapProjectFile:mapProjectFiles) {
                if (StringUtils.isNotBlank(mapProjectFile.getFilePath())) {
                    MinioUtil.removeFile(mapProjectFile.getFilePath());
                }
            }
        }
        // 删除表信息
        mapProjectFileService.deleteByIds(Func.toStrList(ids));
        return R.data(null);
    }

    /**
    * @描述: 单文件下载
    * @入参:
    * @出参:
    * @作者: 代鹏刚
    * @日期: 2021/1/19 18:34
    **/
    @PostMapping("/downloadFile")
    @ApiOperation(value = "单文件下载")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileName", value = "当前文件名称", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "filePath", value = "路径", required = true, dataType = "string", paramType = "query")
    })
    public void downloadFile(String fileName, String filePath, HttpServletResponse response) throws UnsupportedEncodingException {
        // 获取文件后缀
        String fileExtentName = filePath.substring(filePath.lastIndexOf(".") + 1);
        if (!fileName.contains(".")){
            fileName += "." + fileExtentName;
        }
        filePath = URLDecoder.decode(filePath,"utf-8");
        InputStream inputStream = MinioUtil.download(filePath);
        int len = 0;
        byte [] buff=new byte[1024];
        try{
            OutputStream out = new BufferedOutputStream(response.getOutputStream());
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            while((len=inputStream.read(buff))!=-1){
                out.write(buff, 0, len);
            }
            out.flush();
            out.close();
            inputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
    * @描述: 下载目录下所有文件
    * @入参:
    * @出参:
    * @作者: 代鹏刚
    * @日期: 2021/1/19 18:39
    **/
    @PostMapping("/downloadFileFromCatalog")
    @ApiOperation(value = "下载目录下所有文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "目录名称", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "id", value = "节点id", required = true, dataType = "string", paramType = "query")
    })
    public void downloadFileFromCatalog(String name, String id, HttpServletResponse response) {
        //获取父节点下的所有数据
        List<MapProjectFile> treeList = mapProjectFileService.getAllChildrenById(Func.toStrList(id),null);
        List<String> fileUrlList = new ArrayList<>();
        for (MapProjectFile node : treeList) {
            if (node.getType() == 2) {
                fileUrlList.add(node.getFilePath());
            }
        }
        if(fileUrlList.size() > 0){
            MinioUtil.batchDownload(fileUrlList, name, response);
        }
    }

    /**
    * @描述: 附件目录拖拽排序
    * @入参:
    * @出参:
    * @作者: 代鹏刚
    * @日期: 2021/1/19 19:31
    **/
    @PostMapping("/moveSort")
    @ApiOperation(value = "专题目录拖拽排序")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "当前拖拽ID", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "targetId", value = "目标位置ID", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "ahead", value = "是否拖拽到目标位置下方(true:是 false:否)", required = true, dataType = "boolean", paramType = "query")
    })
    public R moveSort(String id , String targetId, Boolean ahead){
        //获取目标数据
        MapProjectFile targetData = mapProjectFileService.getById(targetId);
        //获取所有跟目标同级的数据
        MapProjectFile moveData = mapProjectFileService.getById(id);
        String oldParentId = moveData.getParentId();
        Integer sort = moveData.getSort();
        List<MapProjectFile> mapProjectDirs = (List<MapProjectFile>)mapProjectFileService.listByMap(VerificationUtil.verifyMap("",moveData.getParentId()));
        if(ahead){
            //拖拽至目标下方的情况
            List<MapProjectFile> mapProjectFiles = (List<MapProjectFile>)mapProjectFileService.listByMap(VerificationUtil.verifyMap("",targetData.getId()));
            if(targetData.getType() == 1){
                //如果目标是目录，则将该目录下所有数据序号加一
                if(null != mapProjectFiles && mapProjectFiles.size() > 0){
                    for (MapProjectFile mapProjectFile:mapProjectFiles) {
                        mapProjectFile.setSort(mapProjectFile.getSort() + 1);
                    }
                    mapProjectFileService.updateBatchById(mapProjectFiles);
                }
                //更新拖拽的数据
                moveData.setSort(1);
                moveData.setParentId(targetData.getId());
                mapProjectFileService.updateById(moveData);
            }else {
                //如果目标是附件，则将在附件下方的附件序号加一
                updateSort(moveData,targetData,true);
            }
        }else {
            //拖拽至目标上方的情况
            updateSort(moveData,targetData,false);
        }
        //移动完成后，需要修改被移动数据同级的数据序号
        List<MapProjectFile> saveData = mapProjectDirs.stream().filter(s -> !s.getId().equals(id)).collect(Collectors.toList());
        if(saveData.size() > 0){
            if(!saveData.get(0).getParentId().equals(oldParentId)){
                for (MapProjectFile mapProjectFile:saveData) {
                    if(mapProjectFile.getSort() > sort){
                        mapProjectFile.setSort(mapProjectFile.getSort() - 1);
                    }
                }
                mapProjectFileService.updateBatchById(saveData);
            }
        }
        return R.data(null);
    }
    private void updateSort(MapProjectFile mp, MapProjectFile mapProjectDir, Boolean isAhead){
        //1.查询当前拖拽到的目标父目录下所有的数据
        List<MapProjectFile> mapProjectFileList = (List<MapProjectFile>)mapProjectFileService.listByMap(VerificationUtil.verifyMap("",mapProjectDir.getParentId()));
        if(!isAhead){
            //2.拖拽至目标上方
            //同级拖拽
            if(mp.getParentId().equals(mapProjectDir.getParentId())){
                if(mp.getSort() > mapProjectDir.getSort()){
                    for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                        if(mapProjectDir1.getSort() < mp.getSort() && mapProjectDir1.getSort() >= mapProjectDir.getSort()){
                            mapProjectDir1.setSort(mapProjectDir1.getSort() + 1);
                        }
                    }
                    //3.更新排序
                    mp.setSort(mapProjectDir.getSort());
                }else {
                    for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                        if(mapProjectDir1.getSort() > mp.getSort() && mapProjectDir1.getSort() < mapProjectDir.getSort()){
                            mapProjectDir1.setSort(mapProjectDir1.getSort() - 1);
                        }
                    }
                    //3.更新排序
                    mp.setSort(mapProjectDir.getSort() - 1);
                }
            }else {
                for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                    if(mapProjectDir1.getSort() >= mapProjectDir.getSort()){
                        mapProjectDir1.setSort(mapProjectDir1.getSort() + 1);
                    }
                }
                //3.更新排序
                mp.setSort(mapProjectDir.getSort());
            }
            mapProjectFileService.updateBatchById(mapProjectFileList);
        }else {
            //2.拖拽至目标下方
            //同级拖拽
            if(mp.getParentId().equals(mapProjectDir.getParentId())){
                if(mp.getSort() < mapProjectDir.getSort()){
                    for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                        if(mapProjectDir1.getSort() > mp.getSort() && mapProjectDir1.getSort() <= mapProjectDir.getSort()){
                            mapProjectDir1.setSort(mapProjectDir1.getSort() - 1);
                        }
                    }
                    //3.更新排序
                    mp.setSort(mapProjectDir.getSort());
                }else {
                    for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                        if(mapProjectDir1.getSort() < mp.getSort() && mapProjectDir1.getSort() > mapProjectDir.getSort()){
                            mapProjectDir1.setSort(mapProjectDir1.getSort() + 1);
                        }
                    }
                    //3.更新排序
                    mp.setSort(mapProjectDir.getSort() + 1);
                }
            }else {
                for (MapProjectFile mapProjectDir1:mapProjectFileList) {
                    if(mapProjectDir1.getSort() > mapProjectDir.getSort()){
                        mapProjectDir1.setSort(mapProjectDir1.getSort() + 1);
                    }
                }
                //3.更新排序
                mp.setSort(mapProjectDir.getSort() + 1);
            }
            mapProjectFileService.updateBatchById(mapProjectFileList);
        }
        //4.更新拖拽的数据
        mp.setParentId(mapProjectDir.getParentId());
        mapProjectFileService.updateById(mp);
    }

    /**
    * @描述: minio小文件预览
    * @入参:
    * @出参:
    * @作者: 代鹏刚
    * @日期: 2021/12/22 18:39
    **/
    @RequestMapping(value = "/review/{fileName}" ,method = {RequestMethod.GET,RequestMethod.POST})
    @ApiOperation(value = "从文件服务器预览文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileId", value = "文件id", required = true,dataType = "string", paramType = "query",example = "group1/M00/00/00/CgABPl_9k_yAa9tuAANxrbL9GFU053.txt"),
            @ApiImplicitParam(name = "fileName", value = "文件名", required = true,dataType = "string", paramType = "path")
    })
    public R review(String fileId,HttpServletResponse response,@PathVariable("fileName") String fileName) throws Exception {
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        response.addHeader("Access-Control-Allow-Headers", "Content-Type");
        // 获取文件后缀
        String fileExtentName = fileId.substring(fileId.lastIndexOf(".") + 1);
        if (!fileName.contains(".")){
            fileName += "." + fileExtentName;
        }
        //设置返回文件流格式
        response.setCharacterEncoding("UTF-8");
        String ext = fileExtentName.toLowerCase();
        if (ext.equals("gif")){
            response.setHeader("content-Type", "image/gif");
        }else if (ext.equals("jfif") || ext.equals("jpe") || ext.equals("jpg") || ext.equals("jpeg")){
            response.setHeader("content-Type", "image/jpeg");
        }else if (ext.equals("ico")){
            response.setHeader("content-Type", "image/x-icon");
        }else if (ext.equals("png")){
            response.setHeader("content-Type", "image/png");
        }else if (ext.equals("tif") || ext.equals("tiff") ){
            response.setHeader("content-Type", "image/tiff");
        }else if (ext.equals("xsl") || ext.equals("xslt") || ext.equals("xsd") || ext.equals("xquery") || ext.equals("svg") || ext.equals("xql") || ext.equals("xml")){
            response.setHeader("content-Type", "text/xml");
        }else if (ext.equals("htm") || ext.equals("html") || ext.equals("htx") || ext.equals("jsp") || ext.equals("plg") || ext.equals("xhtml") ){
            response.setHeader("content-Type", "text/html");
        }else if (ext.equals("txt") ){
            response.setHeader("content-Type", "text/plain");
        }else if (ext.equals("pdf") ){
            response.setHeader("content-Type", "application/pdf");
        }else{
            response.setHeader("content-Type", "application/octet-stream");
        }
        response.setHeader("Content-Disposition", "inline" );
        // 下载文件
        InputStream input =  MinioUtil.download(fileId);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] bytes = new byte[1024];
            int len = -1;
            while ((len = input.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
            }
            outputStream.flush();
            outputStream.close();
            input.close();
        } catch (Exception e) {
            return R.fail("文件不存在");
        }
        return null;
    }

    private void prepareData(MapProjectFile mapProjectFile, boolean isSave) {
        if(isSave){
            mapProjectFile.setCreator(getUser().getUserId().toString());
            mapProjectFile.setCreateTime(new Date());
            //防止误修改
            mapProjectFile.setModifier(null);
            mapProjectFile.setModifyTime(null);
        }else{
            mapProjectFile.setModifier(getUser().getUserId().toString());
            mapProjectFile.setModifyTime(new Date());
            //防止误修改
            mapProjectFile.setCreator(null);
            mapProjectFile.setCreateTime(null);
        }
    }
}
